package com.foreveross.crawl.adapter.sub.impl20140402.v3.airchina;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.foreveross.crawl.adapter.CrawlAdapterFactory;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.adapter.sub.impl20140402.v3.AirchinaAdapter;
import com.foreveross.crawl.common.util.DateUtil;
import com.foreveross.crawl.common.util.RegHtmlUtil;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * 国航国际适配器。
 *
 * @author luomingliang@foreveross.com
 * @version 1.0.0
 */
public class AirchinaInterTrip {
	protected Log logger = LogFactory.getLog(getClass());
	/**
	 * ******************************** Params ***********************************************
	 */
	private Long exe_time = 30000L; // 国航屏蔽IP,一定要隔一段时间抓取一次。
	private final Boolean IS_TAX = true; // 是否获取税费（只获取每条航线的最高和最低价格。）
	private final Boolean ONLY_LOWEST_TAX = true; // 是否只抓取最低价格。

	private static final Double domej = 170D; // 国内的税费统一170。
	private static final String CHAR_SET = "UTF-8";
	private static final String REQUEST_HOST = "et.airchina.com.cn";
	private static final String REQUEST_PATH = "/InternetBooking/AirLowFareSearchExternal.do";
	private static final String dataUrl = "http://et.airchina.com.cn/InternetBooking/AirLowFareSearchExt.do";
	private Map<Double, Double> taxMap = Maps.newHashMap();// 保存稅費信息。

	private AirchinaAdapter adapter;
	private TaskModel taskQueue;

	/**
	 * ******************************** Constructor ***********************************************
	 */
	public AirchinaInterTrip(TaskModel taskQueue, AirchinaAdapter adapter) {
		this.taskQueue = taskQueue;
		this.adapter = adapter;
		if (!taskQueue.isUseProxyip()) exe_time = 15000L;// 不使用代理IP的情况下，抓取间隔时间长一点
		else exe_time = 1000L;// 使用代理IP 。
	}

	/**
	 * ******************************** Fetch ***********************************************
	 */

	private URI produceUri() throws URISyntaxException, ParseException {
		Calendar cal = Calendar.getInstance();
		Date gradDate = DateUtils.parseDate(taskQueue.getFlightDate(), "yyyy-MM-dd");
		cal.setTime(gradDate);
		int year = cal.get(Calendar.YEAR);
		int month = cal.get(Calendar.MONTH) + 1;
		int day = cal.get(Calendar.DATE);
		String depPort = taskQueue.getFromCity();
		String arrPort = getToCity();

		List<NameValuePair> qparams = new ArrayList<NameValuePair>();
		if (taskQueue.getIsReturn() == 1) {
			qparams.add(new BasicNameValuePair("tripType", "RT"));
			Date rgradDate = DateUtils.parseDate(taskQueue.getReturnGrabDate(), "yyyy-MM-dd");
			cal.setTime(rgradDate);
			int ryear = cal.get(Calendar.YEAR);
			int rmonth = cal.get(Calendar.MONTH) + 1;
			int rday = cal.get(Calendar.DATE);
			qparams.add(new BasicNameValuePair("inboundOption.departureDay", rday + ""));
			qparams.add(new BasicNameValuePair("inboundOption.departureMonth", rmonth + ""));
			qparams.add(new BasicNameValuePair("inboundOption.departureYear", ryear + ""));
		} else {
			qparams.add(new BasicNameValuePair("tripType", "OW"));
		}
		qparams.add(new BasicNameValuePair("searchType", "FARE"));
		qparams.add(new BasicNameValuePair("flexibleSearch", "false"));
		qparams.add(new BasicNameValuePair("directFlightsOnly", "false"));
		qparams.add(new BasicNameValuePair("fareOptions", "1.FAR.X"));
		qparams.add(new BasicNameValuePair("outboundOption.originLocationCode", depPort));
		qparams.add(new BasicNameValuePair("outboundOption.destinationLocationCode", arrPort));
		qparams.add(new BasicNameValuePair("outboundOption.departureDay", day + ""));
		qparams.add(new BasicNameValuePair("outboundOption.departureMonth", month + ""));
		qparams.add(new BasicNameValuePair("outboundOption.departureYear", year + ""));
		qparams.add(new BasicNameValuePair("outboundOption.departureTime", "NA"));
		qparams.add(new BasicNameValuePair("guestTypes[0].type", "ADT"));
		qparams.add(new BasicNameValuePair("guestTypes[0].amount", "1"));
		qparams.add(new BasicNameValuePair("guestTypes[1].type", "CNN"));
		qparams.add(new BasicNameValuePair("guestTypes[1].amount", "0"));
		qparams.add(new BasicNameValuePair("pos", "AIRCHINA_CN"));
		qparams.add(new BasicNameValuePair("lang", "zh_CN"));
		qparams.add(new BasicNameValuePair("guestTypes[2].type", "INF"));
		qparams.add(new BasicNameValuePair("guestTypes[2].amount", "0"));
		return URIUtils.createURI("http", REQUEST_HOST, -1, REQUEST_PATH, URLEncodedUtils.format(qparams, CHAR_SET), null);
	}

	/**
	 * 抓取数据。
	 * 
	 * @return
	 * @throws Exception
	 */
	public Object fetchData() throws Exception {
		logger.info("进入抓取数据方法 ...");
		String page = "";
		try {
			logger.info("设置Cookies信息，并获取数据。");
			adapter.excuteRequest(new HttpPost(produceUri())); // 设置Cookie信息。
																// 新版的Client自动设置Cookie信息。
			Thread.sleep(exe_time);
			page = adapter.excuteRequest(new HttpGet(dataUrl));// 获取页面
			validateFetch(page); // 验证
			adapter.appendPageContents(page);// 记录源网页
			return this.parseToVo(page);
		} finally {
			logger.info("数据抓取完成!");
			page = null;
		}
	}

	private boolean validateFetch(Object fetchObject) throws Exception {
		if (StringUtils.isEmpty(fetchObject.toString())) {
			throw new Exception("抓取到的页面为空！");
		}
		if (RegHtmlUtil.regMathcher(fetchObject.toString(), "WARNING_NO_RESULTS_FOR_SELECTED_DATES")) {
			throw new FlightInfoNotFoundException("所查找的日期没有对应的航班 ！");
		}
		return true;
	}

	/** 从html字符串解析机票数据 */
	public List<Object> parseToVo(Object fetchObject) throws Exception {
		logger.info("正在解析数据 ：" + taskQueue.getFromCity() + "-" + getToCity() + " begin : (paraseToVo) ):" + DateUtil.formatDay(new Date(), "yyyy-MM-dd hh:mm:ss"));

		List<AbstractPlaneInfoEntity> results = Lists.newArrayList();
		String fetchHtml = (String) fetchObject;

		List<String> shtml = this.getMlementContentReturn(fetchHtml, 0); // 获取去程所有航班
		Set<ReturnDoublePlaneInfoEntity> returnTrips = new HashSet<ReturnDoublePlaneInfoEntity>();
		Map<String, ReturnCabinEntity> lowHighMap = new HashMap<String, ReturnCabinEntity>();

		if (this.taskQueue.getIsReturn() == 1) { // 获取所有回程航班
			List<String> rshtml = this.getMlementContentReturn(fetchHtml, 1); // 获取去程所有航班
			logger.info("正在抓取稅費信息...");
			if (IS_TAX) setTaxPrice(shtml, rshtml); // 往返的时候获取最高和最低税费价格。
			logger.info("稅費抓取完成...");
			this.loadReturnPrice(returnTrips, lowHighMap, rshtml, null);// 也加载了returnTrips，但是不包括下一步产品类型
			if (returnTrips.size() == 0) {
				throw new FlightInfoNotFoundException("没有查到回程航班");
			}
		}
		String cid = null;
		List<String> cids = null;
		if (isHavReturnCabin(lowHighMap)) {
			cids = new Vector<String>(lowHighMap.keySet());
		}

		boolean first = true;
		for (Iterator<String> iterator = shtml.iterator(); iterator.hasNext();) {

			// 因为添加了每个返程最低价格，所以这里的返程对象不能复用，否则设置最低价格的时候会被覆盖。
			Set<ReturnDoublePlaneInfoEntity> returnPlaneInfos = newObjecctSet(returnTrips);

			if (isHavReturnCabin(lowHighMap)) {
				cid = cids.get(new Random().nextInt(cids.size()));
			}
			String html = iterator.next();
			AbstractPlaneInfoEntity planeInfo = null;

			if (first == true && this.taskQueue.getIsReturn() == 1) {
				planeInfo = this.getPlaneInfoEntity(html, lowHighMap, returnPlaneInfos, cid, first);// 第一次获取航班信息的时候要获取返程仓位高低价位下一页信息
				first = planeInfo == null;
			} else {
				planeInfo = this.getPlaneInfoEntity(html, lowHighMap, returnPlaneInfos, cid);
			}
			results.add(planeInfo);
		}

		logger.info("airchina explain " + taskQueue.getFromCity() + "-" + getToCity() + " end : (paraseToVo) ):" + DateUtil.formatDay(new Date(), "yyyy-MM-dd hh:mm:ss"));

		if (taskQueue.getIsReturn() == 0) PlaneInfoEntityBuilder.buildLimitPrice(results);
		else AirchinaAdapterUtil.setLowAndHihtPrice(results, taxMap); // 设置最高和最低价格
																		// 。
		Object ojb = results;
		return (List<Object>) ojb;
	}

	/**
	 * ******************************** Fetch Tax ***********************************************
	 */
	/**
	 * 设置税费。
	 * 
	 * @param goHtml
	 * @param reHtml
	 * @throws InterruptedException
	 */
	private void setTaxPrice(List<String> goHtml, List<String> reHtml) throws InterruptedException {

		List<Map<Double, Integer>> golines = Lists.newArrayList();// 去程
		List<Map<Double, Integer>> relines = Lists.newArrayList();// 回程
		try {

			assemblePriceNo(goHtml, golines);// 组装去程价格和编号信息。
			assemblePriceNo(reHtml, relines);// 组装回程价格和编号信息。

			for (Map<Double, Integer> goline : golines) { // 去程航班集合

				List<Double> prices = new ArrayList<Double>(goline.keySet());
				for (Map<Double, Integer> reline : relines) { // 去程航班價格集合。

					List<Double> reprices = new ArrayList<Double>(reline.keySet());
					for (int i = 0; i < goline.size(); i++) {// 回程航班價格集合。
						for (int j = 0; j < reline.size(); j++) {

							// 每个航班的最低价格和最高价格。
							if ((i == 0 && j == 0) || (!ONLY_LOWEST_TAX && i == goline.size() - 1 && j == reline.size() - 1)) { // 只查詢每個航線每个回程的最高和最低的稅費。
								Double price = prices.get(i) + reprices.get(j);
								// 检查此价格是否已经查询过税费，没有则通过价格编号查询(规则：价格一样的舱位税费也一样)。
								checkPriceHasTax(price, goline.get(prices.get(i)), reline.get(reprices.get(j)));
							}
						}
					}
				}
			}
		} catch (Exception e) {
			golines = null;
			relines = null;
		}
	}

	private void assemblePriceNo(List<String> htmls, List<Map<Double, Integer>> lines) {
		Map<Double, Integer> line = null;
		String tmpHtml = null;
		Document doc = null;
		Elements eles = null;
		String priceno = null;
		String price = null;
		try {
			for (String str : htmls) {
				line = Maps.newTreeMap(); // 用TreeMap排序 。
				tmpHtml = decodeUnicode(str);
				doc = Jsoup.parse("<table>" + tmpHtml + "</table>");
				// flightNo = doc.select("td.colFlight").first().text();
				eles = doc.select("td.colCost table");
				for (Element ele : eles) {
					priceno = ele.select("div.colRadio > input").first().attr("value"); // 价格编号。
					price = ele.select("div.colPrice").first().text().replace(",", ""); // 价格 。
					line.put(Double.parseDouble(price), Integer.parseInt(priceno)); // 把每個航班的價格和價格編號放到Map。
				}
				lines.add(line);
			}
		} finally {
			line = null;
			tmpHtml = null;
			doc = null;
			eles = null;
			priceno = null;
			price = null;
		}
	}

	private void checkPriceHasTax(Double price, Integer gono, Integer reno) {
		Double tax = taxMap.get(price);
		if (tax == null) { // 如果这个价格没有获取过税费则获取税费。
			tax = fetchPrice(gono, reno);
			taxMap.put(price, tax);
		}
		System.out.println("税费信息。：" + tax);
	}

	/**
	 * 获取税费。
	 * 
	 * @param gono 去程价格编号
	 * @param reno 回程价格编号
	 * @return
	 */
	private Double fetchPrice(Integer gono, Integer reno) {
		String taxUrl = "http://et.airchina.com.cn/InternetBooking/AirSelectOWCFlight.do";
		Double taxFee = null;
		Map<String, String> params = Maps.newHashMap();
		params.put("alignment", "horizontal");
		params.put("combinabilityReloadRequired", "true");
		params.put("context", "airSelection");
		params.put("flowStep", "AIR_COMBINABLE_FARE_FAMILIES_SEARCH_RESULTS");
		params.put("isFareFamilySearchResult", "true");
		params.put("selectedFlightIds", String.format("0,%s,1,%s", gono, reno));
		params.put("selectedItineraries", String.format("0,%s", gono));
		params.put("selectedItineraries", String.format("1,%s", reno));
		try {
			Thread.sleep(exe_time);
			String page = adapter.excuteRequest(adapter.getBasePost(taxUrl, params));
			page = RegHtmlUtil.regStr(page, "bottomBot:\\s*?'(.*?)'");
			page = decodeUnicode(page);
			Document doc = Jsoup.parse("<html>" + page + "</html>");
			Elements eles = doc.select("table.detailedPrice td.price > div");

			String tax1 = eles.get(1).text().replace(",", ""); // 稅費
			String tax2 = eles.get(2).text().replace(",", ""); // 稅費
			taxFee = Double.parseDouble(tax1) + Double.parseDouble(tax2);
		} catch (Exception e) {
			logger.error(String.format("获取[%s-%s]最低价格税费信息失败！", gono, reno));
		}
		return taxFee;
	}

	/**
	 * 克隆集合。
	 * 
	 * @param inobjs
	 * @return
	 * @throws Exception
	 */
	private <T> Set<T> newObjecctSet(Set<T> inobjs) throws Exception {
		// 因为添加了每个返程最低价格，所以这里的返回对象不能复用，需要创建新对象，否则设置最低价格的时候混乱。
		Set<T> outobjs = Sets.newHashSet();
		for (T obj : inobjs) {
			outobjs.add((T) BeanUtils.cloneBean(obj));
		}
		return outobjs;
	}

	/**
	 * 是否有返程仓位
	 * 
	 * @param lowHighMap
	 * @return
	 */
	private boolean isHavReturnCabin(Map<String, ReturnCabinEntity> lowHighMap) {
		return lowHighMap != null && lowHighMap.size() != 0;
	}

	/**
	 * 获取返程最低价对象
	 * 
	 * @param returnTrips
	 * @param lowHighMap 最低价跟最高价回程仓位的集合
	 * @param fetchHtml
	 * @param cookie
	 * @return
	 * @throws FlightInfoNotFoundException
	 */
	public Double[] loadReturnPrice(Set<ReturnDoublePlaneInfoEntity> returnTrips, Map<String, ReturnCabinEntity> lowHighMap, List<String> shtml, String cookie) throws FlightInfoNotFoundException {
		if (shtml != null) {
			Integer rowi = -1;
			Map<Double, Integer> priceRow = new HashMap<Double, Integer>();
			for (Iterator<String> iterator = shtml.iterator(); iterator.hasNext();) {
				rowi++;
				String html = iterator.next();
				String tmpHtml = decodeUnicode(html);
				org.jsoup.nodes.Document j = Jsoup.parse("<table>" + tmpHtml + "</table>");
				Elements tmp = j.getElementsByClass("colCost");

				List<String> flightNoList = RegHtmlUtil.retrieveLinks(html, ";return false;\\\\\"\\\\u003e([^\\d][\\w]+?)\\\\u003c", 1);// OK
				if (flightNoList == null) {
					continue;
				}
				if (this.taskQueue.getIsReturn() == 1 && flightNoList.size() > 1) {// 中转不查返程
					// continue;
				}

				Elements priceEle = j.getElementsByClass("colPrice");
				if (priceEle.size() == 0) {
					continue;
				}
				loadgetPriceRow(priceEle, priceRow, rowi);
				Set<ReturnCabinEntity> cabins = this.getReturnCabins(cookie, tmp, lowHighMap);

				ReturnDoublePlaneInfoEntity returnTrip = getReturnTrip(html, j);
				returnTrip.setReturnCabins(cabins);

				returnTrips.add(returnTrip);
			}
		}
		return null;
	}

	/**
	 * 解析返程航班。
	 * 
	 * @param html
	 * @param j
	 * @param prices
	 * @return
	 */
	private ReturnDoublePlaneInfoEntity getReturnTrip(String html, org.jsoup.nodes.Document j) {

		// /航班编号List
		List<String> flightNoList = RegHtmlUtil.retrieveLinks(html, ";return false;\\\\\"\\\\u003e([^\\d][\\w]+?)\\\\u003c", 1);// OK
		if (flightNoList == null) {
			return null;
		}
		if (this.taskQueue.getIsReturn() == 1 && flightNoList.size() > 1) {// 中转不查返程
			// return null;
		}
		String flightNo = RegHtmlUtil.regStr(html, ";return false;\\\\\"\\\\u003e([\\w]+?)\\\\u003c", 1);// OK
		// /起飞时间list
		Elements depTimesList = j.getElementsByClass("colDepart");
		if (depTimesList.size() == 0) {
			return null;
		}

		// 到达时间List
		Elements arrTimesList = j.getElementsByClass("colArrive");
		if (arrTimesList.size() == 0) {
			return null;
		}

		List<ReturnTransitEntity> returnTransits = getReturnTransit(html, j, flightNoList, depTimesList, arrTimesList);

		// 航班类型型号LIST
		List<String> flightTypeNosList = RegHtmlUtil.retrieveLinks(html, "do\\?equipmentType=([\\w]{2,6})\\\\\'", 1); // /OK
		if (flightTypeNosList == null) {
			return null;
		}
		Elements airportInfo = j.getElementsByClass("simpleToolTip");
		List<String> carrierNames = new ArrayList<String>();
		List<String> fromAirs = new ArrayList<String>();
		List<String> toAirs = new ArrayList<String>();
		loadTransit(airportInfo, carrierNames, fromAirs, toAirs);
		ReturnDoublePlaneInfoEntity returnTrip = PlaneInfoEntityBuilder.getReturnTrip(taskQueue, "CA", carrierNames.get(0), carrierNames.get(0), depTimesList.get(0).text(), arrTimesList.get(0).text(), flightNo, null, null, null, null);
		returnTrip.setStartTime(returnTransits.get(0).getStartTime());
		returnTrip.setEndTime(returnTransits.get(returnTransits.size() - 1).getEndTime());
		if (returnTransits.size() > 1) // 设置回程中转
		returnTrip.getReturnTransits().addAll(returnTransits);
		return returnTrip;
	}

	private void loadgetPriceRow(Elements priceEle, Map priceRow, Integer rowi) {
		for (Element p : priceEle) {
			Double price = Double.valueOf(p.text().replace(",", ""));
			if (rowi >= 0) {
				priceRow.put(price, rowi);
			}
		}
	}

	private AbstractPlaneInfoEntity getPlaneInfoEntity(String html, Map<String, ReturnCabinEntity> lowHighMap, Set<ReturnDoublePlaneInfoEntity> returnTrips, String cid) throws Exception {
		return this.getPlaneInfoEntity(html, lowHighMap, returnTrips, cid, false);
	}

	/**
	 * 获取航班信息 如果是查往返 ，排除中转; lowHighMap!=null的时候要获取返程仓位高低价位下一页信息
	 * 
	 * @param html
	 * @param cookie
	 * @param lowHighMap
	 * @param returnTrips
	 * @param cid 随机的第一个仓位id
	 * @return
	 * @throws Exception
	 */
	private AbstractPlaneInfoEntity getPlaneInfoEntity(String html, Map<String, ReturnCabinEntity> lowHighMap, Set<ReturnDoublePlaneInfoEntity> returnTrips, String cid, boolean first) throws Exception {
		AbstractPlaneInfoEntity result = null;
		String tmpHtml = decodeUnicode(html);
		org.jsoup.nodes.Document j = Jsoup.parse("<table>" + tmpHtml + "</table>");
		Elements tmp = j.getElementsByClass("colCost");

		// /航班编号List
		List<String> flightNoList = RegHtmlUtil.retrieveLinks(html, ";return false;\\\\\"\\\\u003e([^\\d][\\w]+?)\\\\u003c", 1);// OK
		if (flightNoList == null) {
			return null;
		}

		// /起飞时间list
		Elements depTimesList = j.getElementsByClass("colDepart");
		if (depTimesList.size() == 0) {
			return null;
		}

		// 到达时间List
		Elements arrTimesList = j.getElementsByClass("colArrive");
		if (arrTimesList.size() == 0) {
			return null;
		}

		List<TransitEntity> transit = getTransit(html, j, flightNoList, depTimesList, arrTimesList);
		if (transit == null) {// 如果是查往返 ，排除中转
			return null;
		}

		String flightNo = RegHtmlUtil.regStr(html, ";return false;\\\\\"\\\\u003e([\\w]+?)\\\\u003c", 1);// OK
		logger.info(flightNo);
		if (StringUtils.isEmpty(flightNo)) {
			return null;
		}

		// 航班类型型号
		String flightTypeNos = RegHtmlUtil.regStr(html, "do\\?equipmentType=([\\w]{2,6})\\\\\'", 1); // /OK
		if (StringUtils.isEmpty(flightTypeNos)) {
			return null;
		}

		if (transit.size() > 1) {// 中转
			result = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, "CA", "国航", "中国国际航空公司", null, null, flightNo, null, null, null, flightTypeNos);
			result.setStartTime(transit.get(0).getStartTime());
			result.setEndTime(transit.get(transit.size() - 1).getEndTime());

			ArrayList<TransitEntity> transitSet = new ArrayList<TransitEntity>(transit);
			PlaneInfoEntityBuilder.setTransits(result, transitSet);
			// System.out.println(flightNo);
		} else {// 非中转
			result = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, "CA", "国航", "中国国际航空公司", depTimesList.get(0).text().trim(), arrTimesList.get(0).text().trim(), flightNo, null, null, null, flightTypeNos);
		}
		DoublePlaneInfoEntity doublePlaneInfoEntity = null;
		if (taskQueue.getIsReturn() == 1) {
			doublePlaneInfoEntity = PlaneInfoEntityBuilder.getDoubleEntity(result);
			doublePlaneInfoEntity.setReturnPlaneInfos(returnTrips);
		}
		Set<CabinEntity> cabins = getCabins(tmp, lowHighMap, doublePlaneInfoEntity, cid, first);
		PlaneInfoEntityBuilder.setCabin(result, cabins);
		return result;
	}

	/**
	 * 不中转的航班返回list的大小为1 返程不查中转，返回list为1
	 * 
	 * @param html
	 * @param j
	 * @param prices
	 * @param arrTimesList
	 * @param depTimesList
	 * @param flightNoList
	 * @return
	 */
	private List<TransitEntity> getTransit(String html, org.jsoup.nodes.Document j, List<String> flightNoList, Elements depTimesList, Elements arrTimesList) {
		List<TransitEntity> transit;
		transit = new ArrayList<TransitEntity>();

		// 航班类型型号LIST
		List<String> flightTypeNosList = RegHtmlUtil.retrieveLinks(html, "do\\?equipmentType=([\\w]{2,6})\\\\\'", 1); // /OK
		if (flightTypeNosList == null) {
			return null;
		}

		// 出发地三字码List
		List<String> fromCityCodeList = RegHtmlUtil.retrieveLinks(html, "&origin=([\\w]+?)&destination", 1);
		if (fromCityCodeList == null) {
			return null;
		}
		// 到达地三字码LIST
		List<String> toCityCodeList = RegHtmlUtil.retrieveLinks(html, "destination=([\\w]+?)&departureDay", 1);
		if (toCityCodeList == null) {
			return null;
		}

		// 机场和承运人信息
		Elements airportInfo = j.getElementsByClass("simpleToolTip");
		List<String> carrierNames = new ArrayList<String>();
		List<String> fromAirs = new ArrayList<String>();
		List<String> toAirs = new ArrayList<String>();
		loadTransit(airportInfo, carrierNames, fromAirs, toAirs);

		for (int k = 0; k < depTimesList.size(); k++) {
			TransitEntity entity = new TransitEntity();
			entity.setCarrierFullName(carrierNames.get(k));
			entity.setCarrierName(carrierNames.get(k));
			// entity.setLowerPrice(prices[0]);
			// entity.setHightPrice(prices[prices.length - 1]);
			entity.setFromAirPortName(fromAirs.get(k));
			entity.setToAirPortName(toAirs.get(k));
			entity.setFlightType(flightTypeNosList.get(k));
			entity.setFlightNo(flightNoList.get(k));
			entity.setFromAirPort(fromCityCodeList.get(k));
			entity.setToAirPort(toCityCodeList.get(k));
			entity.setStartTime(CrawlAdapterFactory.getFlightTime(taskQueue.getFlightDate(), depTimesList.get(k).text()));
			entity.setEndTime(CrawlAdapterFactory.getFlightTime(taskQueue.getFlightDate(), arrTimesList.get(k).text()));
			transit.add(entity);
		}
		return transit;
	}

	/**
	 * 不中转的航班返回list的大小为1 返程不查中转，返回list为1
	 * 
	 * @param html
	 * @param j
	 * @param prices
	 * @param arrTimesList
	 * @param depTimesList
	 * @param flightNoList
	 * @return
	 */
	private List<ReturnTransitEntity> getReturnTransit(String html, org.jsoup.nodes.Document j, List<String> flightNoList, Elements depTimesList, Elements arrTimesList) {
		List<ReturnTransitEntity> retransits = Lists.newArrayList();

		// 航班类型型号LIST
		List<String> flightTypeNosList = RegHtmlUtil.retrieveLinks(html, "do\\?equipmentType=([\\w]{2,6})\\\\\'", 1); // /OK
		if (flightTypeNosList == null) {
			return null;
		}

		// 出发地三字码List
		List<String> fromCityCodeList = RegHtmlUtil.retrieveLinks(html, "&origin=([\\w]+?)&destination", 1);
		if (fromCityCodeList == null) {
			return null;
		}
		// 到达地三字码LIST
		List<String> toCityCodeList = RegHtmlUtil.retrieveLinks(html, "destination=([\\w]+?)&departureDay", 1);
		if (toCityCodeList == null) {
			return null;
		}

		// 机场和承运人信息
		Elements airportInfo = j.getElementsByClass("simpleToolTip");
		List<String> carrierNames = new ArrayList<String>();
		List<String> fromAirs = new ArrayList<String>();
		List<String> toAirs = new ArrayList<String>();
		loadTransit(airportInfo, carrierNames, fromAirs, toAirs);

		for (int k = 0; k < depTimesList.size(); k++) {
			try {
				ReturnTransitEntity reentity = new ReturnTransitEntity();
				reentity.setCarrierFullName(carrierNames.get(k));
				reentity.setFromAirPortName(fromAirs.get(k));
				reentity.setToAirPortName(toAirs.get(k));
				reentity.setFlightType(flightTypeNosList.get(k));
				reentity.setFlightNo(flightNoList.get(k));
				reentity.setFromAirPort(fromCityCodeList.get(k));
				reentity.setToAirPort(toCityCodeList.get(k));
				reentity.setStartTime(CrawlAdapterFactory.getFlightTime(taskQueue.getReturnGrabDate(), depTimesList.get(k).text()));
				reentity.setEndTime(CrawlAdapterFactory.getFlightTime(taskQueue.getReturnGrabDate(), arrTimesList.get(k).text()));
				retransits.add(reentity);
			} catch (Exception e) {
				logger.error("回程航班组装出错：" + e.getMessage());
			}
		}
		return retransits;
	}

	/**
	 * 加载机场和承运人信息
	 * 
	 * @param airportInfo
	 * @param carrierNames
	 * @param fromAirs
	 * @param toAirs
	 */
	private void loadTransit(Elements airportInfo, List<String> carrierNames, List<String> fromAirs, List<String> toAirs) {
		String[] tmpari = null;
		for (int k = 0; k < airportInfo.size(); k++) {
			Element e = airportInfo.get(k);
			/*
			 * 有承运公司 原型是： <div class="simpleToolTip"> <p>承运航空公司：深圳航空有限责任公司</p> </div> <div class="simpleToolTip"> <p> 广州白云机场 (CAN) - 成都双流机场 (CTU)</p> </div> <div class="simpleToolTip"> <p> 这个价格还<b>剩余7
			 * 个座位</b><br /></p> </div> <div class="simpleToolTip"> <p> 这个价格还<b>剩余4 个座位</b><br /></p> </div>
			 */
			if (e.text().indexOf("承运航空公司") != -1) {
				carrierNames.add(e.text());
				while (true) {
					tmpari = airportInfo.get(++k).text().split(" - ");
					if (tmpari.length > 1) {
						fromAirs.add(tmpari[0]);
						toAirs.add(tmpari[1]);
						break;
					}
				}

			} else {
				/*
				 * 无承运公司 原型是 <div class="simpleToolTip"> <p> 广州白云机场 (CAN) - 成都双流机场 (CTU)</p> </div> <div class="simpleToolTip"> <p> 这个价格还<b>剩余7 个座位</b><br /></p> </div>
				 */
				tmpari = airportInfo.get(k).text().split(" - ");
				if (tmpari.length > 1) {
					carrierNames.add("中国国际航空股份有限公司");
					fromAirs.add(tmpari[0]);
					toAirs.add(tmpari[1]);
				}
			}
		}
	}

	/** 获取返程仓位 */
	private Set<ReturnCabinEntity> getReturnCabins(String cookie, Elements tmp, Map<String, ReturnCabinEntity> lowHighMap) {
		// 获取仓位
		Set<ReturnCabinEntity> cabins = new HashSet<ReturnCabinEntity>();
		for (int i = 0; i < tmp.size(); i++) {
			try {
				String price = RegHtmlUtil.regStr(tmp.get(i).toString(), "flightSelectGr_1_([\\d]+?)\">(.*?)</", 2);
				String id = RegHtmlUtil.regStr(tmp.get(i).toString(), "flightSelectGr_1_([\\d]+?)\">(.*?)</", 1);
				if (price != null) {
					ReturnCabinEntity c = new ReturnCabinEntity();
					String cabinType = null;
					String productName = null;
					price = price.replace(",", "");
					if (tmp.size() == 5) {
						cabinType = getCabinType(i);
						productName = getProductName(i);
					} else if (tmp.size() == 8) {
						cabinType = getCabinTypeInte(i);
						productName = getProductNameInte(i);
					}

					c = PlaneInfoEntityBuilder.buildCabinInfo(cabinType, null, productName, null, price, null, null, null, ReturnCabinEntity.class);
					cabins.add(c);
					lowHighMap.put(id, c); // 保存所有仓们信息。
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return cabins;
	}

	/** 获取仓位 */
	private Set<CabinEntity> getCabins(Elements tmp, Map<String, ReturnCabinEntity> lowHighMap, DoublePlaneInfoEntity doublePlaneInfoEntity, String cid, boolean first) {
		// 获取仓位
		Set<CabinEntity> cabins = new HashSet<CabinEntity>();

		for (int i = 0; i < tmp.size(); i++) {
			String cabinType = null;
			String productName = null;
			try {
				String price = RegHtmlUtil.regStr(tmp.get(i).toString(), "flightSelectGr_0_([\\d]+?)\">(.*?)</", 2);
				// String id = RegHtmlUtil.regStr(tmp.get(i).toString(),
				// "flightSelectGr_0_([\\d]+?)\">(.*?)</", 1);

				if (price != null) {
					price = price.replace(",", "");
					if (tmp.size() == 5) {
						cabinType = getCabinType(i);
						productName = getProductName(i);
					} else if (tmp.size() == 8) {
						cabinType = getCabinTypeInte(i);
						productName = getProductNameInte(i);
					}

					CabinEntity c = new CabinEntity();
					c = PlaneInfoEntityBuilder.buildCabinInfo(cabinType, null, productName, null, price, null, null, null, CabinEntity.class);

					if (taskQueue.getIsInternational() == 1) {
						c.setProductName(productName);
					}

					cabins.add(c);
					if (doublePlaneInfoEntity != null && isHavReturnCabin(lowHighMap)) {
						for (ReturnCabinEntity returnCabin : lowHighMap.values()) {
							Double allprice = c.getPrice() + returnCabin.getPrice();
							Double taxprice = taxMap.get(allprice); // 從Map中获取税费。
							Double amount = AirchinaAdapterUtil.addDouble(taxprice, allprice);// 如果稅費為空不計算總價。

							CabinRelationEntity relation = new CabinRelationEntity();
							relation.setCabinId(c.getId());
							relation.setReturnCabinId(returnCabin.getId());
							relation.setTaxesPrice(taxprice); // 税费
							relation.setFullPrice(allprice); // 裸价
							relation.setTotalFullPrice(amount); // 总价
							doublePlaneInfoEntity.getCabinRelations().add(relation);
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return cabins;
	}

	/** 正则匹配得到每一行的记录 */
	private List<String> getMlementContentReturn(String sHtml, int isReturn) {
		int i = 0;
		if (isReturn == 1) {
			i = 1;
		}
		String html = null;
		html = RegHtmlUtil.regStr(sHtml, "tdGroupData\\[" + i + "\\]\\s*?=\\s*?\\{(.+?)\\};", 1, Pattern.CASE_INSENSITIVE | Pattern.COMMENTS | Pattern.DOTALL);
		return RegHtmlUtil.retrieveLinks(html, "\"([-]*[\\d]+)\":\\s*\"(.*?)\\\\n\\\\t\\\\t\\\\t\\\\t\"", 2, Pattern.DOTALL);// ok
	}

	/**
	 * ******************************** Tools ***********************************************
	 */

	/** unicode 转换成 中文 */
	private static String decodeUnicode(String theString) {
		char aChar;
		int len = theString.length();
		StringBuffer outBuffer = new StringBuffer(len);
		for (int x = 0; x < len;) {
			aChar = theString.charAt(x++);
			if (aChar == '\\') {
				aChar = theString.charAt(x++);
				if (aChar == 'u') {
					// Read the xxxx
					int value = 0;
					for (int i = 0; i < 4; i++) {
						aChar = theString.charAt(x++);
						switch (aChar) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							value = (value << 4) + aChar - '0';
							break;
						case 'a':
						case 'b':
						case 'c':
						case 'd':
						case 'e':
						case 'f':
							value = (value << 4) + 10 + aChar - 'a';
							break;
						case 'A':
						case 'B':
						case 'C':
						case 'D':
						case 'E':
						case 'F':
							value = (value << 4) + 10 + aChar - 'A';
							break;
						default:
							throw new IllegalArgumentException("Malformed   \\uxxxx   encoding.");
						}
					}
					outBuffer.append((char) value);
				} else {
					if (aChar == 't') aChar = '\t';
					else if (aChar == 'r') aChar = '\r';
					else if (aChar == 'n') aChar = '\n';
					else if (aChar == 'f') aChar = '\f';
					outBuffer.append(aChar);
				}
			} else outBuffer.append(aChar);
		}
		return outBuffer.toString();
	}

	/**
	 * 国内仓位
	 * 
	 * @param i
	 * @return
	 */
	private String getCabinType(int i) {

		switch (i) {
		case 0:
			return "头等";
		case 1:
			return "公务";
		case 2:
			return "全价";
		case 3:
			return "折扣";
		case 4:
			return "特价";
		default:
			return null;
		}
	}

	private String getProductName(int i) {
		switch (i) {
		case 0:
			return "头等";
		case 1:
			return "公务";
		case 2:
			return "全价";
		case 3:
			return "折扣";
		case 4:
			return "特价";
		default:
			return null;
		}
	}

	/**
	 * 获取国际仓位
	 * 
	 * @param i
	 * @return
	 */
	private String getProductNameInte(int i) {
		switch (i) {
		case 0:
			return "头等";
		case 1:
			return "头等折扣";
		case 2:
			return "公务";
		case 3:
			return "公务折扣";
		case 4:
			return "高端全价";
		case 5:
			return "商旅知音";
		case 6:
			return "折扣经济";
		case 7:
			return "超值特价";
		default:
			return null;
		}
	}

	private String getCabinTypeInte(int i) {
		switch (i) {
		case 0:
		case 1:
			return "头等舱";
		case 2:
		case 3:
			return "公务舱";
		case 4:
		case 5:
		case 6:
		case 7:
			return "经济舱";
		default:
			return null;
		}
	}

	private String getToCity() {
		String toCity = taskQueue.getToCity();
		if (StringUtils.isNotBlank(toCity) && "LON".equalsIgnoreCase(toCity)) {
			toCity = "LHR";
		}
		return toCity;
	}
}
